package com.think.oauth.user.qh.cas;

public abstract class Base64Utils {

	private static final byte[] ENC_TAB_BYTES = new byte[64];

	static {
		int j = 0;
		for (byte i = 'A'; i <= 'Z'; i++) {
			ENC_TAB_BYTES[j] = i;
			j++;
		}
		for (byte i = 'a'; i <= 'z'; i++) {
			ENC_TAB_BYTES[j] = i;
			j++;
		}
		for (byte i = '0'; i <= '9'; i++) {
			ENC_TAB_BYTES[j] = i;
			j++;
		}
		ENC_TAB_BYTES[j++] = '+';
		ENC_TAB_BYTES[j] = '/';
	}

	public static String encodeBase64Str(String str) {
		byte[] bytes = StringUtils.getBytes(str);
		return encodeBase64Str(bytes);
	}

	public static String encodeBase64Str(byte[] bytes) {
		byte[] base64Bytes = encodeBase64(bytes);
		return StringUtils.newString(base64Bytes);
	}

	public static byte[] encodeBase64(byte[] bytes) {
		byte[] base64Bytes;
		int modulus = bytes.length % 3;
		if (modulus == 0) {
			base64Bytes = new byte[(4 * bytes.length) / 3];
		} else {
			base64Bytes = new byte[4 * ((bytes.length / 3) + 1)];
		}
		int dataLength = (bytes.length - modulus);
		int a1;
		int a2;
		int a3;
		for (int i = 0, j = 0; i < dataLength; i += 3, j += 4) {
			a1 = bytes[i] & 0xff;
			a2 = bytes[i + 1] & 0xff;
			a3 = bytes[i + 2] & 0xff;
			base64Bytes[j] = ENC_TAB_BYTES[(a1 >>> 2) & 0x3f];
			base64Bytes[j + 1] = ENC_TAB_BYTES[((a1 << 4) | (a2 >>> 4)) & 0x3f];
			base64Bytes[j + 2] = ENC_TAB_BYTES[((a2 << 2) | (a3 >>> 6)) & 0x3f];
			base64Bytes[j + 3] = ENC_TAB_BYTES[a3 & 0x3f];
		}
		int b1;
		int b2;
		int b3;
		int d1;
		int d2;
		switch (modulus) {
		case 0:
			break;
		case 1:
			d1 = bytes[bytes.length - 1] & 0xff;
			b1 = (d1 >>> 2) & 0x3f;
			b2 = (d1 << 4) & 0x3f;
			base64Bytes[base64Bytes.length - 4] = ENC_TAB_BYTES[b1];
			base64Bytes[base64Bytes.length - 3] = ENC_TAB_BYTES[b2];
			base64Bytes[base64Bytes.length - 2] = '=';
			base64Bytes[base64Bytes.length - 1] = '=';
			break;
		case 2:
			d1 = bytes[bytes.length - 2] & 0xff;
			d2 = bytes[bytes.length - 1] & 0xff;
			b1 = (d1 >>> 2) & 0x3f;
			b2 = ((d1 << 4) | (d2 >>> 4)) & 0x3f;
			b3 = (d2 << 2) & 0x3f;
			base64Bytes[base64Bytes.length - 4] = ENC_TAB_BYTES[b1];
			base64Bytes[base64Bytes.length - 3] = ENC_TAB_BYTES[b2];
			base64Bytes[base64Bytes.length - 2] = ENC_TAB_BYTES[b3];
			base64Bytes[base64Bytes.length - 1] = '=';
			break;
		}
		return base64Bytes;
	}

	private static final byte[] DEC_TAB_BYTES = new byte[128];

	static {
		for (int i = 0; i < 128; i++) {
			DEC_TAB_BYTES[i] = (byte) -1;
		}
		for (int i = 'A'; i <= 'Z'; i++) {
			DEC_TAB_BYTES[i] = (byte) (i - 'A');
		}
		for (int i = 'a'; i <= 'z'; i++) {
			DEC_TAB_BYTES[i] = (byte) (i - 'a' + 26);
		}
		for (int i = '0'; i <= '9'; i++) {
			DEC_TAB_BYTES[i] = (byte) (i - '0' + 52);
		}
		DEC_TAB_BYTES['+'] = 62;
		DEC_TAB_BYTES['/'] = 63;
	}

	public static String decodeBase64Str(String data) {
		byte[] base64Bytes = StringUtils.getBytes(data);
		return decodeBase64Str(base64Bytes);
	}

	public static String decodeBase64Str(byte[] base64Bytes) {
		byte[] bytes = decodeBase64(base64Bytes);
		return StringUtils.newString(bytes);
	}

	public static byte[] decodeBase64(byte[] base64Bytes) {
		byte[] bytes;
		byte b1;
		byte b2;
		byte b3;
		byte b4;
		base64Bytes = discardNonBase64Bytes(base64Bytes);
		if (base64Bytes[base64Bytes.length - 2] == '=') {
			bytes = new byte[(((base64Bytes.length / 4) - 1) * 3) + 1];
		} else if (base64Bytes[base64Bytes.length - 1] == '=') {
			bytes = new byte[(((base64Bytes.length / 4) - 1) * 3) + 2];
		} else {
			bytes = new byte[((base64Bytes.length / 4) * 3)];
		}
		for (int i = 0, j = 0; i < (base64Bytes.length - 4); i += 4, j += 3) {
			b1 = DEC_TAB_BYTES[base64Bytes[i]];
			b2 = DEC_TAB_BYTES[base64Bytes[i + 1]];
			b3 = DEC_TAB_BYTES[base64Bytes[i + 2]];
			b4 = DEC_TAB_BYTES[base64Bytes[i + 3]];
			bytes[j] = (byte) ((b1 << 2) | (b2 >> 4));
			bytes[j + 1] = (byte) ((b2 << 4) | (b3 >> 2));
			bytes[j + 2] = (byte) ((b3 << 6) | b4);
		}
		if (base64Bytes[base64Bytes.length - 2] == '=') {
			b1 = DEC_TAB_BYTES[base64Bytes[base64Bytes.length - 4]];
			b2 = DEC_TAB_BYTES[base64Bytes[base64Bytes.length - 3]];
			bytes[bytes.length - 1] = (byte) ((b1 << 2) | (b2 >> 4));
		} else if (base64Bytes[base64Bytes.length - 1] == '=') {
			b1 = DEC_TAB_BYTES[base64Bytes[base64Bytes.length - 4]];
			b2 = DEC_TAB_BYTES[base64Bytes[base64Bytes.length - 3]];
			b3 = DEC_TAB_BYTES[base64Bytes[base64Bytes.length - 2]];
			bytes[bytes.length - 2] = (byte) ((b1 << 2) | (b2 >> 4));
			bytes[bytes.length - 1] = (byte) ((b2 << 4) | (b3 >> 2));
		} else {
			b1 = DEC_TAB_BYTES[base64Bytes[base64Bytes.length - 4]];
			b2 = DEC_TAB_BYTES[base64Bytes[base64Bytes.length - 3]];
			b3 = DEC_TAB_BYTES[base64Bytes[base64Bytes.length - 2]];
			b4 = DEC_TAB_BYTES[base64Bytes[base64Bytes.length - 1]];
			bytes[bytes.length - 3] = (byte) ((b1 << 2) | (b2 >> 4));
			bytes[bytes.length - 2] = (byte) ((b2 << 4) | (b3 >> 2));
			bytes[bytes.length - 1] = (byte) ((b3 << 6) | b4);
		}
		return bytes;
	}

	private static byte[] discardNonBase64Bytes(byte[] datas) {
		byte[] temp = new byte[datas.length];
		int bytesCopied = 0;
		for (byte data : datas) {
			if (isValidBase64Byte(data)) {
				temp[bytesCopied++] = data;
			}
		}
		byte[] newData = new byte[bytesCopied];
		System.arraycopy(temp, 0, newData, 0, bytesCopied);
		return newData;
	}

	private static boolean isValidBase64Byte(byte b) {
		if (b == '=') {
			return true;
		} else if ((b < 0) || (b >= 128)) {
			return false;
		} else if (DEC_TAB_BYTES[b] == -1) {
			return false;
		}
		return true;
	}
	
	public static void main(String[] args) {
		String obj = encodeBase64Str("portaladmin");
		System.out.println("obj === " + obj);
	}
	
}